// manager.cc

#include "manager.h"
#include "synch.h"
#include "sharedDefinitions.h"

/* The Manager has 3 jobs: 
 * (1) keep track of the total sales for the grocery store,
 * (2) keep track of the length of Customer lines, and 
 * (3) wake up StockBoys for either restocking shelves or helping Customers. 
 *
 * The Manager never goes on break. He just keeps checking 
 * to see if there is something to do. 
 * - Every so often the Manager will display the total sales
 *   of the entire store up to that point. 
 * - Whenever the Manager gets a call from a Clerk, they are
 *   to send a Clerk who is on break to an available Cash Register. 
 * - The Manager also wakes up StockBoys when the service button 
 *   has been pushed by a Customer.
 * - The manager will regularly empty the CashRegister drawers 
 *   to keep track of total sales.
 *  
 * There is only one Manager.
 */

//Default constructor
Manager::Manager(){
  //_totalSales = -1;
}
//Constructor for manager
Manager::Manager(Locks * theLocks){
  //_totalSales = 0;
  _theLocks = theLocks;
}

//Manager goes to work!
void Manager::DoWork(){
    bool doWork = TRUE;
    bool collectAllTills = false;
    for (int i = 0; i < 20; i++)
    	currentThread->Yield();
	while(doWork){
		//srand (time(NULL));
		int randomVar = (rand()%100)+1;
		if (randomVar <= 20)
		{
#ifdef PRINT_OUTPUT_MANAGER
			printf("------- Total Sale of the store until now is $[%.2f]-------\n", GetTotalSales());
#endif
		}
#ifndef IGNORE_REGISTERS
		if (randomVar<=2 || collectAllTills)
		{
			#ifdef PRINT_EXTRA_LOG_MANAGER
				printf("MANAGER# lets empty till\n");
			#endif
			for (int registerId = 0; registerId < MAX_CLERKS; registerId++)
			{
				Lock* registerStatusLock = _theLocks->_registerLock[registerId];
				Lock* registerLock = _theLocks->_fromRegisterInteractionLock[registerId];
				Lock* peopleWaiting = _theLocks->_peopleWaitingForCashRegiterLock[registerId]; //NEW
				Condition* registerStatusCV = _theLocks->_registerCV[registerId];
				int currentRegister = registerId;
				#ifdef PRINT_EXTRA_LOG_CASH_REGISTER
				#ifdef PRINT_EXTRA_LOG_MANAGER
					printf("MANAGER#: Lets acquire the lock for Cash register%i\n", registerId);
				#endif
				#endif
				bool shouldIgnoreCurrentCashRegiter = false;
				#ifdef PRINT_EXTRA_LOG_MANAGER
					printf("MANAGER: is going to acquire the lock for register#%i\n", registerId);
				#endif
				registerStatusLock->Acquire();
				#ifdef PRINT_EXTRA_LOG_MANAGER
					printf("MANAGER: acquired the lock for register#%i\n", registerId);
				#endif
				if (_theLocks->_managerEmtpyingTill[registerId] == -2 ) //cash register not initialized yet
				{
					shouldIgnoreCurrentCashRegiter = true;
				}
				else
				{
					while (_theLocks->_managerEmtpyingTill[currentRegister] != -1)
					{
						if (collectAllTills==TRUE)
						{
							peopleWaiting->Acquire(); //NEW
							registerStatusLock->Release(); //NEW
							#ifdef PRINT_EXTRA_LOG_MANAGER
								printf("MANAGER: is going to wait for  register#%i\n", registerId);
							#endif
							_theLocks->_managerWantsToInteract[currentRegister] = 0;
							do
							{
								(_theLocks->_peopleWaitingForCashRegister[currentRegister])++;
								_theLocks->_peopleWaitingForCashRegiterCV[currentRegister]->Wait(peopleWaiting); //NEW
							} while(_theLocks->_managerWantsToInteract[currentRegister] != 1);
							#ifdef PRINT_EXTRA_LOG_MANAGER
								printf("MANAGER: Just got woken up from cash regiter #%i\n", registerId);
							#endif
							_theLocks->_managerWantsToInteract[currentRegister] = -1;
							registerStatusLock->Acquire(); //NEW
							peopleWaiting->Release(); //NEW
						}
						else
						{
							//cash register busy, lets continue to other cash registers
							shouldIgnoreCurrentCashRegiter = true;
							break;
						}
					}

				}
				if (shouldIgnoreCurrentCashRegiter==true)
				{
					registerStatusLock->Release();
				}
				else
				{
					_theLocks->_registerCV[registerId]->Signal(registerStatusLock);
					registerLock->Acquire();
					_theLocks->_managerEmtpyingTill[registerId] = 1;
					registerStatusLock->Release();
					//Lets wait for cash register till to put its till in shared memory
					#ifdef PRINT_EXTRA_LOG_CASH_REGISTER
					#ifdef PRINT_EXTRA_LOG_MANAGER
						printf("MANAGER#: Lets wait for Cash register %i to provide its till\n", registerId);
					#endif
					#endif
					_theLocks->_fromRegisterInteractionCV[registerId]->Wait(registerLock);
					double tillAmount = _theLocks->_moneyToTransfer[registerId];
					IncreaseSales(tillAmount);
					//Pickup the till balance
					#ifdef PRINT_OUTPUT_MANAGER
						printf("Manager emptied Cash Register [%i] drawer.\n", registerId);
						printf("Manager has total sale of $[%.2f].\n", GetTotalSales());
					#endif
					_theLocks->_toRegisterInteractionCV[registerId]->Signal(registerLock);
					registerLock->Release();
				}
			}
		}
#endif
		// check the clerk lines to see if any Clerks signaled us for help
		#ifdef PRINT_EXTRA_LOG_MANAGER
			printf("MANAGER: is going to acquire signalManagerClerkhelp \n");
		#endif
		_theLocks->_signalManagerClerkHelp->Acquire();
		#ifdef PRINT_EXTRA_LOG_MANAGER
			printf("MANAGER: acquired signalManagerClerkhelp \n");
		#endif
			int i =0;
		for( i = 0; i < MAX_CLERKS; i++) {
			// did a clerk signal us?
			if(_theLocks->_storeNeedsMoreClerks[i] == TRUE) {
				// tell the observer

				#ifdef PRINT_OUTPUT_MANAGER
					printf("Manager got a call from Clerk [%i]\n", i);
				#endif
				// turn off the clerk's signal
				_theLocks->_storeNeedsMoreClerks[i] = FALSE;
				// get the lock for waking up a clerk
				_theLocks->_clerksOnBreakLock->Acquire();
				// check to see if there are any clerks to awaken
				int clerksOnBreak = _theLocks->_clerksOnBreakMV;
				if(clerksOnBreak > 0)
				{
					#ifdef PRINT_OUTPUT_MANAGER
										printf("Manager wakes up Clerk.\n");
					#endif
					// decrement the counter
					_theLocks->_clerksOnBreakMV --;
					// signal the next Clerk to Wake up
					_theLocks->_clerksOnBreakCV->Signal(_theLocks->_clerksOnBreakLock);
					// release the lock for waking a clerk
				}
				_theLocks->_clerksOnBreakLock->Release();
				break;
			}
		}
		// we're done looking at the signals from the clerk
		_theLocks->_signalManagerClerkHelp->Release();
		//
		// Lets see if customers need to work with clerks but all of the are in break
		//

		Lock* clerkLineLock = _theLocks->_clerkLinelock;
		Lock* waitForClerksToReturn = _theLocks->_customerNeedsClerkAssitanceLock;
		waitForClerksToReturn->Acquire();
		if (_theLocks->_customersNeedingAsistanceMV > 0)
		{
			#ifdef PRINT_EXTRA_LOG_MANAGER
					printf("MANAGER: There are %i customers waiting for clerk\n", _theLocks->_customersNeedingAsistanceMV);
			#endif
			// get the lock for waking up a clerk
			_theLocks->_clerksOnBreakLock->Acquire();
			// check to see if there are any clerks to awaken
			if(_theLocks->_clerksOnBreakMV > 0) {
			#ifdef PRINT_OUTPUT_MANAGER
				printf("Manager wakes up Clerk.\n");
			#endif
				// decrement the counter
				_theLocks->_clerksOnBreakMV --;
				// signal the next Clerk to Wake up
				_theLocks->_clerksOnBreakCV->Signal(_theLocks->_clerksOnBreakLock);
			}
			else
			{
				#ifdef PRINT_EXTRA_LOG_MANAGER
					printf("MANAGER Cant wake up any Clerk since no one is in break.\n");
				#endif
			}
			_theLocks->_clerksOnBreakLock->Release();
		}
		waitForClerksToReturn->Release();
		//
		//
		// Lets check on stockboys and see if we need call any to return from break
		//if any customer is waiting for an item to be restocked OR
	   //  any customer is waiting in service area go ahead and wake up a stockboy
		bool anyItemNeedsToBeRestocked = false;
		Lock* reStockLock  = _theLocks->_customerWaitingForReStockLock;
#ifdef PRINT_LOCKS_STOCKBOY
		//printf("Manager reStockLock->Acquire()!\n");
#endif
		reStockLock->Acquire();
#ifdef PRINT_LOCKS_STOCKBOY
		//printf("Manager reStockLock->Acquire() Done!\n");
#endif
		for (int cnt = 0 ; cnt < MAX_ITEMS; cnt ++)
		{
			if (_theLocks->_customersWaitingForRestock[cnt]>0
					&& _theLocks->_stockBoyReStockingItem[cnt]==NOT_FOUND)
			{
				anyItemNeedsToBeRestocked = true;
			}
		}
#ifdef PRINT_LOCKS_STOCKBOY
		//printf("Manager reStockLock->Relase()!\n");
#endif
		reStockLock->Release();
#ifdef PRINT_LOCKS_STOCKBOY
		//printf("Manager reStockLock->Release() Done!\n");
#endif

		Lock* customersWaitingLock = _theLocks->_customersWaitingInAisleToAskQuestionLock[SERVICE_AREA_LINE];
		#ifdef PRINT_EXTRA_LOG_MANAGER
			printf("MANAGER: Going to acquired the customers waiting lock\n");
		#endif
		customersWaitingLock ->Acquire();
		int customersInServiceArea = _theLocks->_customersWaitingInAisleToAskQuestionMV[SERVICE_AREA_LINE];
		customersWaitingLock ->Release();
		if (anyItemNeedsToBeRestocked   ||
				customersInServiceArea > 0)
		{
			//Waking up a stock boy
			Lock* stockBoysBreakLock =_theLocks->_stockBoyBreakLock;
			stockBoysBreakLock->Acquire();
			if (_theLocks->_stockBoyOnBreakMV>0)
			{
				_theLocks->_stockBoysOnBreakCV->Signal(stockBoysBreakLock);
				#ifdef PRINT_OUTPUT_MANAGER
					printf("Manager wakes up stock boy.\n");
				#endif
				_theLocks->_stockBoyOnBreakMV--;
			}
			stockBoysBreakLock->Release();
		}
		for (int m=0;m <100; m++)
			currentThread->Yield();

        _theLocks->_occupancyLock->Acquire();
        if(_theLocks->_customersInStore == 0) {
            _theLocks->_occupancyLock->Release();
            if (!collectAllTills)
            {
            	_theLocks->_clerksOnBreakLock->Acquire();
				// check to see if there are still working
				if(_theLocks->_clerksOnBreakMV == MAX_CLERKS)
				{
					_theLocks->_clerksOnBreakLock->Release();
					_theLocks->_stockBoyBreakLock->Acquire();
					if (_theLocks->_stockBoyOnBreakMV == MAX_STOCKBOYS)
					{
						_theLocks->_stockBoyBreakLock->Release();
						//Everyone is out, we can collect all of the tills now
						collectAllTills = true;
					}
					else
					{
						_theLocks->_stockBoyBreakLock->Release();
					}
				}
				else
				{
					_theLocks->_clerksOnBreakLock->Release();
				}
            }
            else
            {
				#ifdef PRINT_EXTRA_LOG_MANAGER
            		printf("MANAGER: no one in the store, manager can close up.\n");
				#endif
				for (int tmp =0; tmp< MAX_CLERKS;tmp++)
				{
					printf("Total Sale from Cash Register [%i] is $[%.2f]\n", tmp, _theLocks->registerSales[tmp]);
				}
				printf("Total Sale of the entire store is $[%.2f]\n", _theLocks->totalStoreSales);
            	return;
            }
        } else {
            _theLocks->_occupancyLock->Release();
        }
	}
 }

//Displays the total sales of the store
void Manager::displayTotalSales(){

}

//Empties a given cash register
void Manager::emptyRegister(CashRegister * cashRegister){
  //printf("Manager emptied Cash Register [%d] drawer", cashRegister->getId());
  //printf("Manager has total sale of $[%f]", cashRegister->EmptyTill());
}
 
//Is called when the line is long to wake up a clerk
void Manager::clerkNeedsAssistance(Clerk * clerk, CashRegister * cashRegisters[]){
  //printf("Manager got a call from Clerk [%d]",clerk->getId());
  

}

//Wakes up a stock boy when service button presse
void customerNeedsAssistance(Stockboy * stockboys[], Customer * customer){

}
